package com.logisticsrepair.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.logisticsrepair.entity.ApplyMaterialEntity;
import com.logisticsrepair.mapper.ApplyMaterialMapper;
import com.logisticsrepair.service.ApplyMaterialService;
import com.logisticsrepair.util.RedisUtil;
import com.logisticsrepair.util.responseUtil.ResponseEnum;
import com.logisticsrepair.util.responseUtil.ResponseUtil;
import com.logisticsrepair.websocket.PaySocketServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import redis.clients.jedis.Jedis;

import java.util.*;

@RestController
@Slf4j
@RequestMapping("/PaymentScanCode")
public class PaymentScanCode {
    Jedis redis = RedisUtil.getRedis();

    private static final String APPID = "wx9b04d88d2bf7f251";

    private static final String APPSECRET = "8b79027ddf3f0d229c075d7301a219d4";

    @Autowired
    ApplyMaterialService applyMaterialService;

    @RequestMapping("/wxLogin")
    public ResponseUtil wxLogin(@RequestBody String body) {
        String code = JSONObject.parseObject(body).getString("code");
        // 获取openid和sessionKey
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + APPID + "&secret=" + APPSECRET + "&js_code=" + code + "&grant_type=authorization_code";
        String post = HttpUtil.post(url, "");

        JSONObject wxLoginResult = JSONObject.parseObject(post);
        System.out.println(wxLoginResult);
        Map<Object, Object> map = new HashMap<>();
        if (wxLoginResult.containsKey("errcode")) {
            map.put("errcode", wxLoginResult.getString("errcode"));
            //HttpStatus: http://www.httpstatus.cn/
            return ResponseUtil.setE(ResponseEnum.FAIL).setData(map);
        }

        String md5Hex = DigestUtil.md5Hex(post);

        String openid = wxLoginResult.getString("openid");
        if (!redis.hexists("UserBalance", openid)) {
            redis.hset("UserBalance", openid, String.valueOf(0));
            map.put("balance", 0);
        } else {
            map.put("balance", redis.hget("UserBalance", openid));
        }

        redis.setex(md5Hex, 60 * 60 * 2, wxLoginResult.toJSONString());
        map.put("token", md5Hex);
        return ResponseUtil.setE(ResponseEnum.OK).setData(map);
    }

    /**
     *
     * @param token
     * @return {openid, session_key}
     */
    public HashMap getTokenInfo(String token){
        redis.select(0);
        if(redis.exists(token)) return JSONObject.parseObject(redis.get(token), HashMap.class);
        return null;
    }

    @RequestMapping("/recharge")
    public ResponseUtil recharge(@RequestBody String body, @RequestHeader("token") String token) {
        HashMap<String, Object> tokenInfo = getTokenInfo(token);
        if (tokenInfo.isEmpty()) return ResponseUtil.setE(ResponseEnum.LOGIN_FAILURE).setMsg("身份凭证过期");

        String openid = tokenInfo.get("openid").toString();

        int balance = NumberUtil.parseInt(redis.hget("UserBalance", openid));
        balance += JSONObject.parseObject(body).getShort("money");
        redis.hset("UserBalance", openid, StrUtil.toString(balance));
        tokenInfo.clear();
        tokenInfo.put("balance", balance);
        return ResponseUtil.setE(ResponseEnum.OK).setData(tokenInfo);
    }

    @RequestMapping("/account/refresh")
    public ResponseUtil accountRef(@RequestHeader("token") String token) {
        HashMap<String, Object> tokenInfo = getTokenInfo(token);
        if (tokenInfo == null) return ResponseUtil.setE(ResponseEnum.LOGIN_FAILURE).setMsg("身份凭证过期");

        String balance = redis.hget("UserBalance", tokenInfo.get("openid").toString());
        return ResponseUtil.setE(ResponseEnum.OK).setData(balance);
    }

    @Autowired
    ApplyMaterialMapper applyMaterialMapper;

    @RequestMapping("/order/pay")
    public ResponseUtil orderPay(@RequestBody Map<String, Object> map, @RequestHeader("token") String token) {
        HashMap<String, Object> tokenInfo = getTokenInfo(token);
        String openid = tokenInfo.get("openid").toString();
        /**
         * map:
         * "{
         *      "expireMillis":7200000,
         *      "totalPrice":32.5,
         *      "uuid":"9496fc2d36d4483295036f4764e23844",
         *      "timeMillis":1650877963899
         *  }"
         */
        //验证token
        if (tokenInfo.isEmpty()) return ResponseUtil.setE(ResponseEnum.LOGIN_FAILURE).setMsg("身份凭证过期");
        if(map.isEmpty()) return ResponseUtil.setE(ResponseEnum.PARAMS_ERROR).setMsg("参数错误");

        HashMap<String, Object> order = JSONObject.parseObject(map.get("order").toString(), HashMap.class);
        String uuid = order.get("uuid").toString();

        if(!redis.exists(uuid)) return ResponseUtil.setE(ResponseEnum.FAIL).setMsg("订单已失效");

        order.clear();
        order = JSONObject.parseObject(redis.get(uuid), HashMap.class);

        float totalPrice = NumberUtil.round(NumberUtil.parseFloat(order.get("totalPrice").toString()), 1).floatValue();
        float UserBalance = NumberUtil.parseFloat(redis.hget("UserBalance", openid));
        double sub = NumberUtil.sub(UserBalance, totalPrice);
        if(sub < 0) return ResponseUtil.setE(ResponseEnum.FAIL).setMsg("余额不足");
        redis.hset("UserBalance",openid, String.valueOf(sub));

        List<String> applyIds = JSONArray.parseArray(order.get("applyIds").toString(), String.class);
        ArrayList<ApplyMaterialEntity> entities = new ArrayList<>();
        DateTime date = DateUtil.date();
        for (String applyId : applyIds) {
            ApplyMaterialEntity applyMaterialEntity = new ApplyMaterialEntity();
            applyMaterialEntity.setApplyId(applyId);
            applyMaterialEntity.setChargeStatus(true);
            applyMaterialEntity.setChargeTime(date);

            entities.add(applyMaterialEntity);
        }
        applyMaterialService.updateBatchById(entities);

        String userOpenId = applyMaterialMapper.queryUserOpenidByApplyId(entities.get(0).getApplyId());

        List<ApplyMaterialEntity> applyMaterialEntities = applyMaterialService.queryUserWaitChargeOrder(userOpenId);

        createConsumptionLog(uuid, openid,totalPrice,date, entities);

        PaySocketServer.sendMessage(ResponseUtil.setE(ResponseEnum.OK).setMsg("支付成功").setData(applyMaterialEntities).toJSONString(),
                order.get("socket").toString());
        redis.del(uuid);
        return ResponseUtil.setE(ResponseEnum.OK).setMsg("支付成功").setData(sub);
    }

    private void createConsumptionLog(String uuid, String openid, float totalPrice, DateTime date, List<ApplyMaterialEntity> applyMaterialEntities) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("uuid", uuid);
        map.put("orderId", applyMaterialEntities.get(0).getOrderId());
        map.put("totalPrice", totalPrice);
        map.put("applyTime", applyMaterialEntities.get(0).getApplyTime());
        map.put("chargeTime", date);
        /**
         * 方案一、取openid前8位 + # + 随机数 = key
         */
        /*String key = new StringBuffer().append(openid.substring(0, 8))
                .append("#")
                .append(RandomUtil.randomInt(999)).toString();*/

        /**
         * 方案一、openid前8位 + # + UUID前4位 + # + 支付（当前）日期 = key
         *
         * UUID前4位：区分每一笔订单。因为同一天可能有多笔订单，加上UUID可以区分订单，订单号唯一
         * 支付（当前）日期：后台刷新不需查询所有记录，查询当天即可
         *
         * 前端缓存为空时（首次访问），请求后台。之后前端会设置缓存，不会每次都请求后台，直至用户手动刷新
         * 请求后台查询用户订单，只需要查询当天的即可，不需要全重新查询。前端再做重复数据处理
         * 匹配模式 openidz#*#支付（当前）日期
         */

        String key = new StringBuffer().append(openid.substring(0, 8))
                .append("#")
                .append(uuid.substring(0, 4))
                .append("#")
                .append(DateUtil.format(DateUtil.date(), "yyyyMMdd")).toString();

        Jedis redis = RedisUtil.getRedis();
        redis.select(6);
        redis.setex(key, 60 * 60 * 24 * 7, JSON.toJSONString(map));
    }

    @RequestMapping("/account/consumptionLog")
    public Object consumptionLog(@RequestHeader("token") String token) {
        HashMap<String, Object> tokenInfo = getTokenInfo(token);
        //验证token
        if (tokenInfo == null) return ResponseUtil.setE(ResponseEnum.LOGIN_FAILURE).setMsg("身份凭证过期");

        String openid = tokenInfo.get("openid").toString().substring(0, 8);

        Jedis redis = RedisUtil.getRedis();
        redis.select(6);

        //用户第一次获取数据，需要返回所有记录
        Set<String> keys = redis.keys(openid + "*");

        List<String> res = redis.mget(keys.toArray(new String[keys.size()]));
        System.out.println(res);
        return ResponseUtil.setE(ResponseEnum.OK).setData(res);
    }

}
